package com.bjpowernode.core.aspect;

import com.bjpowernode.annotation.DataScope;
import com.bjpowernode.entity.TRole;
import com.bjpowernode.entity.TUser;
import com.bjpowernode.entity.query.BaseQuery;
import com.bjpowernode.util.LoginInfoUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Component
@Aspect
public class DataScopeAspect {

    /**
     * 需求：管理员需要看见所有人的信息，其他人只能看自己的
     * 思路：其实也有别的方法（做一些复杂的数据库操作），但是此次使用的是AOP。
     * <p>
     * 下面介绍这里的思路
     * 首先可以通过AOP获得目标方法的参数，并对其进行改造(改造前后该参数（类）都会指向同一的地址，因此在AOP中对参数内容进行改造是会影响到方法结果的)，
     * 这里就是通过注解标记目标方法（mapper上的方法），并且改造mapper的参数，使用${}这种注入SQL的方式实现目的。
     * 至此，问题变得简单了，只需要获取当前用户的角色，如果没有“admin”权限，直接拼接字符串“ tu.id != x”，并将字符串注入到SQL里面就行了
     *
     * 1，创建自定义注解，包含两个参数：表名和列名
     * 2，确认获取角色的方法，
     * 3，写切面类，以被注解的方法为切入点，获取用户id->获取角色列表->检查是否有“admin”角色->编辑被切入的方法的参数内容使用SQL注入
     * <p>
     * 拓展：AOP+自定义注解和AOP+Mapper可以弄出很多花样
     */
    //切入点: 是看有没有@DataScope这个注解
    @Around(value = "@annotation(com.bjpowernode.annotation.DataScope)")
    public Object process(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("==========环绕前==========");
        //在要执行的sql语句的后边拼接一个过滤条件（如果是管理员就不拼接，如果不是管理员就拼接）：  where tu.id = 2，  where tu.id = 3

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        //拿到方法上的注解
        DataScope dataScope = methodSignature.getMethod().getDeclaredAnnotation(DataScope.class);
        String tableAlias = dataScope.tableAlias();//表别名 tu
        String columnName = dataScope.columnName();//表字段 id

        // where tu.id = 3

        //拿到当前用户的身份认证信息
        TUser tUser = LoginInfoUtil.getCurrentLoginUser();

        //拿到用户的角色
        List<TRole> roleList = tUser.getRoleList();
        List<String> roleNameList = new ArrayList<>();
        for (TRole role : roleList) {
            roleNameList.add(role.getRole()); //英文的角色名
        }

        /**
         * 另一种获取是否包含admin权限的方法
         * //anyMatch是匹配任意的意思
         * boolean flag = roleList.stream().anyMatch(tRole -> tRole.getRole().equals("admin"));
         *
         */

        if (!roleNameList.contains("admin")) {//前面是一个“非”，不包含admin角色，那说明是一个普通用户，只查当前用户自己的数据，否则查所有数据
            Object params = joinPoint.getArgs()[0]; //拿目标方法的第一个参数
            if (params instanceof BaseQuery) {
                BaseQuery baseQuery = (BaseQuery) params;
                //select * from t_user tu  where  tu.id = 2 （普通用户：于嫣）
                //and tu.id = 2
                //使用这种方式进行字符串拼接
                baseQuery.setFilterSQL(" and " + tableAlias + "." + columnName + " = " + tUser.getId());
                System.out.println("拼接的SQL过滤条件：" + baseQuery.getFilterSQL());
            }
        }

        //执行目标方法，也就是执行 tUserMapper.selectUsers(BaseQuery baseQuery);
        Object result = joinPoint.proceed();

        System.out.println("==========环绕后==========");
        return result;
    }


}
